﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Security.Cryptography;
using System.Net.Mail;

using ERPStore.Web.Html;
using ERPStore.Web.Routing;
using ERPStore.Extensions;
using ERPStore.Web.Extensions;

using CloudSoft.Extensions;

namespace ERPStore.Web.Controllers
{
	/// <summary>
	/// Controlleur pour les reglements
	/// </summary>
	public class CheckoutController : StoreController
	{
		public CheckoutController(
			ERPStore.Services.ICartService cartService
			, ERPStore.Services.ISalesService salesService
			, ERPStore.Services.IAccountService accountService
			, ERPStore.Services.IEmailerService emailerService
			, Services.IEmailTemplateService emailTemplateService
			, ERPStore.Services.IIncentiveService incentiveService
			, ERPStore.Services.CryptoService cryptoService
			, ERPStore.Services.IOffersService offerService
			)
		{
			this.CartService = cartService;
			this.SalesService = salesService;
			this.AccountService = accountService;
			this.EmailerService = emailerService;
			this.EmailTemplateService = emailTemplateService;
			this.IncentiveService = incentiveService;
			this.CryptoService = cryptoService;
			this.OfferService = offerService;
		}

		protected ERPStore.Services.ICartService CartService { get; private set; }
		protected ERPStore.Services.ISalesService SalesService { get; private set; }
		protected ERPStore.Services.IAccountService AccountService { get; private set; }
		protected ERPStore.Services.IEmailerService EmailerService { get; private set; }
		protected Services.IEmailTemplateService EmailTemplateService { get; private set; }
		protected ERPStore.Services.IIncentiveService IncentiveService { get; private set; }
		protected ERPStore.Services.CryptoService CryptoService { get; private set; }
		protected ERPStore.Services.IOffersService OfferService { get; private set; }

		[ActionFilters.TrackerActionFilter]
		public virtual ActionResult Declined()
		{
			Logger.Warn("Transaction declined");
			ViewData["message"] = "Erreur lors de la transaction";
			var viewName = GlobalConfiguration.Configuration.Settings.Payment.DeclinedView
				?? "~/views/checkout/paymentdeclined.cshtml";
			return View(viewName);
		}

		[ActionFilters.TrackerActionFilter]
		public virtual ActionResult Error()
		{
			Logger.Warn("Transaction error");
			ViewData["message"] = "Erreur lors de la transaction";
			var view = GlobalConfiguration.Configuration.Settings.Payment.ErrorView
				?? "~/views/checkout/paymenterror.cshtml";
			return View(view);
		}

		[ActionFilters.TrackerActionFilter]
		public virtual ActionResult Canceled()
		{
			Logger.Warn("Transaction canceled");
			ViewData["message"] = "Annulation de la transaction";
			var view = GlobalConfiguration.Configuration.Settings.Payment.CanceledView
				?? "~/views/checkout/paymentcanceled.cshtml";
			return View(view);
		}

		[ActionFilters.ZeroCacheActionFilter]
		public ActionResult AjaxChangeConveyor(string conveyorIndex)
		{
			if (conveyorIndex.IsNullOrTrimmedEmpty())
			{
				return new EmptyResult();
			}
			var index = 0;
			int.TryParse(conveyorIndex, out index);
			var cart = CartService.GetCurrentCart(User);
			if (cart == null)
			{
				return new EmptyResult();
			}

			var conveyorList = SalesService.GetConveyorList(cart);

			if (conveyorList == null
				|| index > conveyorList.Count)
			{
				return new EmptyResult();
			}
			var conveyor = conveyorList[index];
			cart.Conveyor = conveyor;

			PrepareCart(cart);

			using (var ts = TransactionHelper.GetNewReadCommitted())
			{
				CartService.Save(cart);
				ts.Complete();
			}

			return Json("ok", JsonRequestBehavior.AllowGet);
		}

		[ERPStore.Web.Controllers.ActionFilters.ZeroCacheActionFilter]
		[ERPStore.Web.Controllers.ActionFilters.TrackerActionFilter]
		public virtual ActionResult Confirmation()
		{
			var cart = CartService.GetCurrentCart(User);
			if (cart == null
				|| cart.ItemCount == 0)
			{
				return RedirectToERPStoreRoute(ERPStore.Web.Routing.ERPStoreRoutes.HOME);
			}

			var user = User.CurrentUser;

			Logger.Info("checkout confirmation with visitor : {0}", User.VisitorId);

			if (user != null)
			{
				if (cart.DeliveryAddress == null
				|| cart.BillingAddress == null)
				{
					Logger.Warn("checkout confirmation without deliveryaddress or billingaddress with visitor : {0}", User.VisitorId);
					return RedirectToERPStoreRoute(ERPStore.Web.Routing.ERPStoreRoutes.CHECKOUT);
				}
			}
			else
			{
				var registration = AccountService.GetRegistrationUser(User.VisitorId);
				if (registration == null)
				{
					Logger.Warn("checkout confirmation without deliveryaddress or billingaddress with visitor : {0}", User.VisitorId);
					return RedirectToERPStoreRoute(ERPStore.Web.Routing.ERPStoreRoutes.CHECKOUT);
				}
				user = AccountService.CreateUserFromRegistration(registration);
				cart.BillingAddress = user.DefaultAddress;
				cart.DeliveryAddress = user.LastDeliveredAddress;
			}

			if (cart.PaymentModeName.IsNullOrTrimmedEmpty())
			{
				Logger.Warn("checkout confirmation without paymentmodename with visitor : {0}", User.VisitorId);
				return RedirectToERPStoreRoute(ERPStore.Web.Routing.ERPStoreRoutes.CHECKOUT_PAYMENT);
			}

			var paymentList = SalesService.GetPaymentList(cart, User);
			var selectedPayment = paymentList.SingleOrDefault(i => i.Name.Equals(cart.PaymentModeName, StringComparison.InvariantCultureIgnoreCase));

			if (selectedPayment == null)
			{
				Logger.Warn("checkout confirmation with bad paymentmodename with visitor : {0}", User.VisitorId);
				return RedirectToERPStoreRoute(ERPStore.Web.Routing.ERPStoreRoutes.CHECKOUT_PAYMENT);
			}

			PrepareCart(cart);

			ViewData.Model = cart;
			var viewName = string.Format("~/views/checkout/{0}", selectedPayment.ConfirmationViewName);
			return View(viewName);
		}

		// For bots only
		public ActionResult TrackGoToPaid()
		{
			return new EmptyResult();
		}

		[ActionFilters.TrackerActionFilter]
		[HttpPost]
		public ActionResult TrackGoToPaid(string cartCode, string paymentName)
		{
			var @event = new Events.GoToPaidEvent()
			{
				CartCode = cartCode,
				PaymentModeName = paymentName
			};
			EventPublisherService.PublishAsync(@event);
			return new EmptyResult();
		}

		protected ERPStore.Models.ISaleDocument ProcessResponse(string cartCode, string paymentModeName, string transactionId, object serverResponse, out bool isNewUser, out string password)
		{
			var order = ProcessResponse(cartCode, out isNewUser, out password, paymentModeName);
			if (order == null)
			{
				var x = new Exception("Order conversion exception, payment aborted");
				x.Data.Add("CartCode", cartCode);
				x.Data.Add("PaymentMode", paymentModeName);
				x.Data.Add("TransactionId", transactionId);
				try
				{
					x.Data.Add("ServerData", serverResponse.SerializeToXml());
				}
				catch
				{
				}
				x.Data.Add("Stack", System.Environment.StackTrace);
				Logger.Fatal(x);
				return null;
			}
			SalesService.AddPaymentToOrder((ERPStore.Models.Order)order, paymentModeName, transactionId, serverResponse);
			return order;
		}
		
		protected ERPStore.Models.ISaleDocument ProcessResponse(string cartCode, out bool isNewUser, out string password, string paymentModeName)
		{
			var cart = CartService.GetCartByCode(cartCode);
			ERPStore.Models.ISaleDocument order = null;
			isNewUser = false;
			password = null;
			if (cart == null)
			{
				Logger.Error("Tentative de paiement avec un panier inconnu : {0}", cartCode);
				return null;
			}

			// La reponse à déjà été traitée
			if (cart.ConvertedEntityId.HasValue)
			{
				Logger.Error(string.Format("Tentative de paiement avec un panier deja converti : {0} {1}", cartCode, cart.ConvertedEntityId.Value));
				return null;
			}

			ERPStore.Models.User user = null;
			ERPStore.Models.RegistrationUser registration = null;

			// Dans le cas d'un achat sans inscription
			if (cart.CustomerId.GetValueOrDefault(0) == 0)
			{
				Logger.Info("order anonymous with {0}", paymentModeName);
				registration = AccountService.GetRegistrationUser(cart.VisitorId);
				if (registration == null)
				{
					// Traitement en cours par un autre thread
					throw new Exception(string.Format("Unknown cart owner : {0} payment mode : {1}", cart.Code, paymentModeName));
				}

				isNewUser = true;
				password = registration.Password;

				if (registration.IsSameBillingAddress)
				{
					registration.BillingAddressCity = registration.ShippingAddressCity;
					registration.BillingAddressCountryId = registration.ShippingAddressCountryId;
					registration.BillingAddressRecipientName = registration.ShippingAddressRecipientName;
					registration.BillingAddressRegion = registration.ShippingAddressRegion;
					registration.BillingAddressStreet = registration.ShippingAddressStreet;
					registration.BillingAddressZipCode = registration.ShippingAddressZipCode;
				}
				user = AccountService.RegisterUser(registration);

				// Affectation de l'adresse de livraison
				cart.DeliveryAddress = new ERPStore.Models.Address();
				cart.DeliveryAddress.City = registration.ShippingAddressCity;
				cart.DeliveryAddress.CountryId = registration.ShippingAddressCountryId;
				cart.DeliveryAddress.RecipientName = registration.ShippingAddressRecipientName;
				cart.DeliveryAddress.Region = registration.ShippingAddressRegion;
				cart.DeliveryAddress.Street = registration.ShippingAddressStreet;
				cart.DeliveryAddress.ZipCode = registration.ShippingAddressZipCode;

				// Affectation de l'adresse de facturation
				cart.BillingAddress = user.DefaultAddress;
				cart.CustomerId = user.Id;
			}
			else
			{
				user = AccountService.GetUserById(cart.CustomerId.Value);
				int deliveryAddressId = 0;
				if (cart.DeliveryAddress != null)
				{
					deliveryAddressId = cart.DeliveryAddress.Id;
				}
				Logger.Info("order with user : {0}, deliveryAddressId : {1}", user.FullName, deliveryAddressId);
			}

			try
			{
				SalesService.ProcessExport(cart, User);
				// création de la commande
				order = SalesService.CreateOrderFromCart(user, cart, paymentModeName);
				Logger.Debug("Add payment to order N°{0}", order.Code);
				using (var ts = TransactionHelper.GetNewReadCommitted())
				{
					cart.ConvertedEntityId = order.Id;
					cart.PaymentModeName = paymentModeName;
					CartService.Save(cart);
					ts.Complete();
				}
			}
			catch (Exception ex)
			{
				LogError(Logger, ex);
				return null;
			}

			if (registration != null)
			{
				AccountService.CloseRegistrationUser(registration.VisitorId, user.Id);
			}

			EventPublisherService.PublishAsync<Events.OrderCreatedEvent>(new Events.OrderCreatedEvent()
			{
				Order = order,
				Cart = cart,
			});

			return order;
		}

		/*
		protected void SendPaymentByCardFailed(ERPStore.Models.OrderCart cart, string message)
		{
			ERPStore.Models.User user = null;
			string email = null;
			string fullName = null;
			if (cart.CustomerId.GetValueOrDefault(0) != 0)
			{
				user = AccountService.GetUserById(cart.CustomerId.Value);

				if (user != null
					&& user.Email.IsNullOrTrimmedEmpty())
				{
					return;
				}
				email = user.Email;
				fullName = user.FullName;
			}
			else
			{
				var registrationUser = AccountService.GetRegistrationUser(cart.VisitorId);
				if (registrationUser == null)
				{
					return;
				}
				email = registrationUser.Email;
				fullName = registrationUser.FullName;
			}

			var mailKey = new
			{
				CartCode = cart.Code,
				Email = email,
				FullName = fullName,
				ExpirationDate = DateTime.Now,
				Message = message,
			};

			var cryptoService = GlobalConfiguration.Configuration.DependencyResolver.GetService<ERPStore.Services.CryptoService>();
			var encrytpedMailKey = cryptoService.Encrypt(mailKey);
			string encrytpedEmailUrl = Url.RouteERPStoreUrl(ERPStoreRoutes.EMAILER, new { action = "DirectSendPaymentByCardFailed", key = encrytpedMailKey, });
			encrytpedEmailUrl = encrytpedEmailUrl.FullUrl();

			dynamic emailView = EmailTemplateService.CreateEmailView(ERPStore.GlobalConfiguration.Configuration.Settings.Emailer.PaymentFailedView, cart);
			emailView.WebSiteSettings = ERPStore.GlobalConfiguration.Configuration.Settings;
			emailView.EncryptedUrl = encrytpedEmailUrl;
			emailView.FullName = fullName;
			emailView.Message = message;
			emailView.Email = email;

			var emailMessage = EmailTemplateService.CreateEmailMessage(emailView);

			EmailerService.Send(emailMessage);
		}

		protected void SendCustomerOrderConfirmation(ERPStore.Models.ISaleDocument order, string password)
		{
			if (order.User.Email.IsNullOrTrimmedEmpty())
			{
				return;
			}

			var mailKey = new
			{
				Code = order.Code,
				Type = (order is ERPStore.Models.Order) ? "order" : "quote",
				Salt = DateTime.Now,
			};

			var cryptoService = GlobalConfiguration.Configuration.DependencyResolver.GetService<ERPStore.Services.CryptoService>();
			var encrytpedMailKey = cryptoService.Encrypt(mailKey);
			string encrytpedEmailUrl = string.Format("http://{0}{1}", HttpContext.Request.Url.Host, Url.RouteERPStoreUrl(ERPStoreRoutes.EMAILER, new { action = "DirectOrderConfirmation", key = encrytpedMailKey, }));

			dynamic emailView = EmailTemplateService.CreateEmailView(ERPStore.GlobalConfiguration.Configuration.Settings.Emailer.OrderConfirmationView, order);
			emailView.WebSiteSettings = ERPStore.GlobalConfiguration.Configuration.Settings;
			emailView.EncryptedUrl = encrytpedEmailUrl;
			emailView.FullName = order.User.FullName;
			emailView.Password = password;

			var emailMessage = EmailTemplateService.CreateEmailMessage(emailView);
			EmailerService.Send(emailMessage);
		}

		protected void SendAdminCustomerOrderConfirmation(ERPStore.Models.ISaleDocument order, string cartId, string ip)
		{
			if (order.User.Email.IsNullOrTrimmedEmpty())
			{
				return;
			}

			var urlHelper = new UrlHelper(ControllerContext.RequestContext);
			var type = (order is ERPStore.Models.Order) ? "order" : "quote";

			var mailKey = new
			{
				Code = order.Code,
				Type = type,
				Salt = DateTime.Now,
			};

			var encryptedTicket = CryptoService.EncryptOrderConfirmation(order.Code, DateTime.Now.AddDays(10), false);
			string encryptedUrl = string.Format("http://{0}{1}{2}", Request.Url.Host, urlHelper.RouteERPStoreUrl(ERPStoreRoutes.ORDER_DETAIL), encryptedTicket);

			var cart = CartService.GetCartByCode(cartId);

			dynamic emailView = EmailTemplateService.CreateEmailView(ERPStore.GlobalConfiguration.Configuration.Settings.Emailer.AdminOrderConfirmationView, order);
			emailView.WebSiteSettings = ERPStore.GlobalConfiguration.Configuration.Settings;
			emailView.FullName = order.User.FullName;
			emailView.OrderCart = cart;
			emailView.IP = ip;
			emailView.EncryptedUrl = encryptedUrl;

			var emailMessage = EmailTemplateService.CreateEmailMessage(emailView);

			EmailerService.Send(emailMessage);
		}
		 */ 

		protected void PrepareCart(ERPStore.Models.OrderCart cart)
		{
			foreach (var item in cart.Items)
			{
				OfferService.ApplyOffers(item.Product, User.CurrentUser);
			}
			var user = User.CurrentUser;
			// Affectation de l'adresse avant le calcul des frais
			if (user != null)
			{
				if (cart.DeliveryAddress == null)
				{
					cart.DeliveryAddress = user.LastDeliveredAddress;
				}
			}
			else
			{
				var registrationUser = AccountService.GetRegistrationUser(cart.VisitorId);
				if (registrationUser != null)
				{
					cart.DeliveryAddress = registrationUser.GetShippingAddress();
				}
			}
			SalesService.CalculateShippingFee(cart, User);
			SalesService.ProcessExport(cart, User);
			CartService.ApplyProductStockInfoList(cart);
		}

	}
}
